
// ======================
// 类型定义
// ======================
interface ReviewRecord {
  timestamp: Date;
  preRecall: number; // 0-1（本次复习前，可以回忆出多少，即过去记忆内容存留的百分比）
  postRetention: number; // 0-1（本次通过记忆后，记忆材料的程度达到了多少）
  studyDuration: number; // 本次记忆的时间，分钟。
  interval: number;  // 预计下次回复的时间间隔，毫秒。
}

interface StaticDifficultyMaterial {
  id: string; // 材料的唯一id。
  fixedDifficulty: number;  // 0-100（材料的难度，100表示很难相信自己能记住，0表示已经牢牢的记住了，用户预设）
  aversionFactor: number;   // 0-1（用户主观厌恶度，0表示很喜欢这个材料，1表示讨厌这些东西）
  intrinsicComplexity: number; // 0-1（自动分析值）
}

interface ReviewPlan {
  date: Date;
  intensity: number;
  expectedRetention: number;
  optimalWindow: [number, number]; // 当日最佳记忆时段 [开始小时, 结束小时]
}

interface ChronoConfig {
  latitude: number;         // 纬度（用于季节计算）
  chronotype: 'morning' | 'night' | 'neutral'; // 作息类型
  localTimezone: string;    // 时区 例如 "Asia/Shanghai"
}

// ======================
// 基础调度器（含时间生物学整合）
// ======================
class ChronoMemoryScheduler {
  protected history: ReviewRecord[] = [];
  protected chronoConfig: ChronoConfig;

  constructor(config: ChronoConfig) {
    this.chronoConfig = config;
  }

  // 核心时间生物学因子
  protected temporalFactor(date: Date): number {
    return this.seasonalFactor(date) * this.dailyRhythm(date) * this.chronotypeFactor(date);
  }

  /**
   * 季节调整（北半球模型）
   * 季节影响	0.85 + 0.15*sin(太阳高度角)	季节性情感障碍研究
   * 冬季自动增加10-15%复习频率
   */
  private seasonalFactor(date: Date): number {
    const dayOfYear = this.getDayOfYear(date);
    const latRad = this.chronoConfig.latitude * Math.PI / 180;
    
    // 太阳高度角计算
    const declination = 0.4095 * Math.sin(2 * Math.PI * (dayOfYear - 81) / 368);
    const sunHeight = Math.sin(latRad) * Math.sin(declination) +
      Math.cos(latRad) * Math.cos(declination);
    
    // 光照影响公式
    return 0.85 + 0.15 * sunHeight;
  }

  /**
   * 日节律曲线（基于体温相位）
   * 日节律	0.7 + 0.3*(体温相位^1.5)	体温与认知表现相关性
   */
  private dailyRhythm(date: Date): number {
    const localHour = this.getLocalHour(date);
    // 体温相位模型（峰值在16:00）
    const tempPhase = 0.5 * Math.cos(2 * Math.PI * (localHour - 16) / 24) + 0.5;
    return 0.7 + 0.3 * Math.pow(tempPhase, 1.5);
  }

  /**
   * 作息类型补偿
   * 作息补偿	晨型+20%/夜型+25%	睡眠医学学会指南
   */
  private chronotypeFactor(date: Date): number {
    const localHour = this.getLocalHour(date);
    switch(this.chronoConfig.chronotype) {
      case 'morning': 
        return localHour >= 5 && localHour < 9 ? 1.2 : 0.95; // 早上5-9点，记忆效果最好
      case 'night':
        return localHour >= 20 && localHour < 23 ? 1.25 : 0.9; // 晚上8-11点，记忆效果最好
      default:
        return 1.0;
    }
  }

  // 工具方法
  private getDayOfYear(date: Date): number {
    const start = new Date(date.getFullYear(), 0, 0);
    return Math.floor((date.getTime() - start.getTime()) / 86400000);
  }

  private getLocalHour(date: Date): number {
    return parseInt(date.toLocaleTimeString('en-US', { 
      timeZone: this.chronoConfig.localTimezone,
      hour12: false 
    }).split(':')[0]);
  }
}

// ======================
// 静态难度调度器（含时间优化）
// ======================
class EnhancedMemoryScheduler extends ChronoMemoryScheduler {
  private readonly MAX_INTERVAL = 365 * 3; // 最长复习间隔时间为3年
  private readonly MAX_REVIEWS = 50; // 最多复习50次

  // 难度-时间综合模型
  calculateNextInterval(
    preRecall: number,
    postRetention: number,
    studyDuration: number,
    material: StaticDifficultyMaterial
  ): number {
    const base = this.baseInterval(preRecall, postRetention, studyDuration);
    const timeFactor = this.temporalFactor(new Date());
    return this.applyAdjustments(base, material, timeFactor);
  }

  // 生成智能复习计划
  generatePlan(material: StaticDifficultyMaterial): ReviewPlan[] {
    const plan: ReviewPlan[] = [];
    let currentDate = new Date();

    for (let i = 0; i < this.MAX_REVIEWS; i++) {
      const optimalWindow = this.calculateOptimalWindow(currentDate);
      const interval = this.calculatePhaseAwareInterval(currentDate, material);
      
      currentDate = new Date(currentDate.getTime() + interval * 86400000);
      
      plan.push({
        date: currentDate,
        intensity: this.calculateIntensity(material, interval),
        expectedRetention: this.predictRetention(interval, material),
        optimalWindow
      });

      if (interval > this.MAX_INTERVAL) break;
    }

    return plan;
  }

  // ======================
  // 私有核心方法
  // ======================
  private baseInterval(
    preRecall: number,
    postRetention: number,
    studyDuration: number
  ): number {
    const gain = postRetention - preRecall;
    const efficiency = 1 - Math.exp(-studyDuration / 30);    
    return Math.max(0.1, 0.5 * Math.pow(gain, 1.5) * (1 + Math.log1p(efficiency)));
  }

  private applyAdjustments(
    base: number,
    material: StaticDifficultyMaterial,
    timeFactor: number
  ): number {
    const difficultyFactor = this.difficultyFactor(material.fixedDifficulty);
    const aversionFactor = 1 + 0.4 * Math.tanh(material.aversionFactor * 3); // 用户主观厌恶度，0表示很喜欢这个材料，1表示讨厌这些东西
    const complexityFactor = 1 + 0.2 * material.intrinsicComplexity;

    return Math.min(
      this.MAX_INTERVAL,
      base * difficultyFactor * aversionFactor * complexityFactor * timeFactor
    );
  }

  // 材料越难记忆，则下次的复习时间应该越长
  private difficultyFactor(difficulty: number): number {
    return difficulty < 30 ? 1 :
      difficulty < 60 ? 1 + 0.03 * Math.pow(difficulty - 30, 1.2) :
      1.5 + 0.05 * Math.pow(difficulty - 60, 1.5);
  }

  private calculatePhaseAwareInterval(currentDate: Date, material: StaticDifficultyMaterial): number {
    const base = material.fixedDifficulty < 40 ? 0.5 : 1;
    const phaseBoost = this.temporalFactor(currentDate) > 1.1 ? 0.9 : 1.1;
    return base * phaseBoost;
  }

  private calculateIntensity(material: StaticDifficultyMaterial, interval: number): number {
    const difficulty = material.fixedDifficulty;
    return 0.3 + 0.7 / (1 + Math.exp(-0.1 * (difficulty - 70 + interval/3)));
  }

  private predictRetention(interval: number, material: StaticDifficultyMaterial): number {
    const stability = 25 * (1 + 0.7 * Math.tanh(interval/30));
    return Math.pow(1 + interval / stability, -0.5) * 0.9;
  }

  private calculateOptimalWindow(date: Date): [number, number] {
    const candidateHours = [6, 9, 15, 18, 21];
    const scores = candidateHours.map(h => {
      const testDate = new Date(date);
      testDate.setHours(h, 0, 0, 0);
      return this.temporalFactor(testDate);
    });
    const bestHour = candidateHours[scores.indexOf(Math.max(...scores))];
    return [bestHour - 1, bestHour + 2]; // 3小时最佳窗口
  }
}

// ======================
// 使用示例
// ======================
const userConfig: ChronoConfig = {
  latitude: 31.2304,       // 上海纬度
  chronotype: 'morning',
  localTimezone: 'Asia/Shanghai'
};

const mathMaterial: StaticDifficultyMaterial = {
  id: "calculus_101",
  fixedDifficulty: 75,
  aversionFactor: 0.6,
  intrinsicComplexity: 0.85
};

const scheduler = new EnhancedMemoryScheduler(userConfig);

// 计算带时间优化的间隔
const nextInterval = scheduler.calculateNextInterval(
  0.4, 0.8, 25, mathMaterial
);
console.log(`优化后的间隔: ${nextInterval.toFixed(2)} 天`);

// 生成智能复习计划
const plan = scheduler.generatePlan(mathMaterial);
console.log("推荐复习计划:");
plan.forEach((p, i) => {
  console.log(`第${i+1}次: ${p.date.toLocaleDateString()} ${p.optimalWindow[0]}:00-${p.optimalWindow[1]}:00`);
});











